package com.powernode.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powernode.constant.OrderConstant;
import com.powernode.constant.OrderStatusConstant;
import com.powernode.constant.QueueConstant;
import com.powernode.domain.*;
import com.powernode.dto.ChangeDto;
import com.powernode.dto.EsChange;
import com.powernode.dto.OverTimerOrder;
import com.powernode.dto.SkuChang;
import com.powernode.feign.OrderBasketFeign;
import com.powernode.feign.OrderProdFeign;
import com.powernode.feign.OrderUserAddrFeign;
import com.powernode.service.OrderItemService;
import com.powernode.service.OrderService;
import com.powernode.mapper.OrderMapper;
import com.powernode.utils.PowerAuthMenuUtil;
import com.powernode.vo.OrderConfirm;
import com.powernode.vo.OrderVo;
import com.powernode.vo.ShopOrder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order>
        implements OrderService {

    @Autowired
    private OrderUserAddrFeign orderUserAddrFeign;

    @Autowired
    private OrderProdFeign orderProdFeign;

    @Autowired
    private OrderBasketFeign orderBasketFeign;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderItemService orderItemService;
    @Value("${wx.templateId}")
    private String templateId;
    @Value("${wx.accessTokenUrl}")
    private String accessTokenUrl;

    /**
     * 两种情况
     * 1.从购物车点击提交订单
     * 1.1 此时需要根据店铺id分类 prod和sku进行实际
     * 1.2 删除掉购物车数据
     * 2.直接从商品点击提交订单
     * 2.只会有一个商品 直接生成该商品的sku价格和prod
     *
     * @param orderConfirm
     * @return
     */
    @Override
    public OrderVo confirm(OrderConfirm orderConfirm) {
        OrderVo orderVo = new OrderVo();
        //根据用户id获取到该用户的默认地址
        String wxUserId = PowerAuthMenuUtil.getWxUserId();
        //使用feign调用basket的地址
        UserAddr userAddr = orderUserAddrFeign.loadUserAddrById(wxUserId);
        orderVo.setUserAddr(userAddr);
        //进行判断是从购物车点击的提交还是直接从商品
        if (CollectionUtil.isEmpty(orderConfirm.getBasketIds())) {
            //basketids为空 商品点击
            submitProdOrder(orderVo, orderConfirm.getOrderItem());
        } else {
            //购物车点击的
            submitBasketOrder(orderVo, orderConfirm.getBasketIds());
        }
        return orderVo;
    }


    /**
     * 购物车提交订单
     *
     * @param orderVo
     * @param basketIds
     */
    private void submitBasketOrder(OrderVo orderVo, List<Long> basketIds) {
        if (CollectionUtil.isNotEmpty(basketIds)) {
            //通过baskids获取到skuids和prodids和shopids
            List<Basket> baskets = orderBasketFeign.loadProdById(basketIds);
            List<Long> prodIds = baskets.stream().map(Basket::getProdId).collect(Collectors.toList());
            List<Long> skuIds = baskets.stream().map(Basket::getSkuId).collect(Collectors.toList());

            //查询对应的商品和sku
            List<Prod> prodList = orderProdFeign.loadProdById(prodIds);
            List<Sku> skuList = orderProdFeign.loadSkuById(skuIds);

            //获取对应的关系
            Map<Long, Prod> prodMap = prodList.stream().collect(Collectors.toMap(Prod::getProdId, prod -> prod));
            Map<Long, Sku> skuMap = skuList.stream().collect(Collectors.toMap(Sku::getSkuId, sku -> sku));

            //定义集合来保存价格和数量
            ArrayList<BigDecimal> price = new ArrayList<>();
            ArrayList<Integer> count = new ArrayList<>();

            //按照店铺进行分组
            Map<Long, List<Basket>> shopMap = baskets.stream().collect(Collectors.groupingBy(Basket::getShopId));
            ArrayList<ShopOrder> shopOrderArrayList = new ArrayList<>();
            //偏离按照商品分组的map
            shopMap.forEach((shopId, basketList) -> {
                ShopOrder shopOrder = new ShopOrder();
                ArrayList<OrderItem> orderItemArrayList = new ArrayList<>(basketList.size());
                //变量购物车列表
                basketList.stream().forEach(basket -> {
                    OrderItem orderItem = new OrderItem();
                    orderItem.setProdCount(basket.getBasketCount());
                    Prod prod = prodMap.get(basket.getProdId());
                    Sku sku = skuMap.get(basket.getSkuId());
                    BeanUtil.copyProperties(prod, orderItem);
                    BeanUtil.copyProperties(sku, orderItem);
                    orderItemArrayList.add(orderItem);
                    Integer oneCount = basket.getBasketCount();
                    //该商品买了几件
                    count.add(oneCount);
                    //该商品购买的总价格
                    price.add(sku.getPrice().multiply(new BigDecimal(oneCount)));
                });
                shopOrder.setShopCartItemDiscounts(orderItemArrayList);
                shopOrderArrayList.add(shopOrder);
            });
            orderVo.setShopCartOrders(shopOrderArrayList);
            //数量总和
            Integer counts = count.stream().reduce(Integer::sum).get();
            //价格总和
            BigDecimal total = price.stream().reduce(BigDecimal::add).get();

            //运费
            BigDecimal transfee = new BigDecimal(8);
            //最终金额
            BigDecimal actualTotal = new BigDecimal(0);
            //满减
            BigDecimal shopReduce = new BigDecimal(0);
            if (total.intValue() >= 50) {
                actualTotal = total;
                transfee = new BigDecimal(0);
                //优惠8元
                shopReduce = new BigDecimal(8);
            } else {
                actualTotal = transfee.add(total);
            }
            //设置最终价格
            orderVo.setActualTotal(actualTotal);
            //设置优惠
            orderVo.setTransfee(transfee);
            //设置总价格
            orderVo.setTotalMoney(total);
            //设置优惠
            orderVo.setShopReduce(shopReduce);
            //设置数量
            orderVo.setTotalCount(counts);
        }

    }

    /**
     * 商品提交订单
     *
     * @param orderVo
     * @param orderItem
     */
    private void submitProdOrder(OrderVo orderVo, OrderItem orderItem) {
        //通过id获取到prod和sku
        Long prodId = orderItem.getProdId();
        Long skuId = orderItem.getSkuId();
        Prod prod = orderProdFeign.loadProdById(Arrays.asList(prodId)).get(0);
        Sku sku = orderProdFeign.loadSkuById(Arrays.asList(skuId)).get(0);
        //将信息拷贝到orderItem中
        BeanUtil.copyProperties(prod, orderItem);
        BeanUtil.copyProperties(sku, orderItem);
        ShopOrder shopOrder = new ShopOrder();
        shopOrder.setShopCartItemDiscounts(Arrays.asList(orderItem));
        orderVo.setShopCartOrders(Arrays.asList(shopOrder));

        //获取到单价
        BigDecimal price = sku.getPrice();
        //获取购买的总价 单价*数量
        Integer one = orderItem.getProdCount();
        BigDecimal total = price.multiply(new BigDecimal(one));
        //运费
        BigDecimal transfee = new BigDecimal(8);
        //最终金额
        BigDecimal actualTotal = new BigDecimal(0);
        //满减
        BigDecimal shopReduce = new BigDecimal(0);
        if (total.intValue() >= 50) {
            actualTotal = total;
            transfee = new BigDecimal(0);
            //优惠8元
            shopReduce = new BigDecimal(8);
        } else {
            actualTotal = transfee.add(total);
        }
        orderVo.setActualTotal(actualTotal);
        orderVo.setTransfee(transfee);
        orderVo.setTotalMoney(total);
        orderVo.setShopReduce(shopReduce);
        orderVo.setTotalCount(one);
    }


    /**
     * 1.生成订单（雪花算法）
     * 2.删除购物车（从购物车点击的）
     * 3.修改库存 （数据库 es）
     * 4.保存订单信息 order orderitem
     * 5.超时订单
     * 6.未支付消息提示
     *
     * @param orderVo
     * @return
     */
    @Override
    public String submit(OrderVo orderVo) {
        String wxUserId = PowerAuthMenuUtil.getWxUserId();
        //1.生成订单 唯一
        String orderNo = createOrderNo();
        //2.删除购物车信息
        removeBasket(orderVo, wxUserId);
        //修改库存(数据库） 并获得到修改的id和数量
        ChangeDto changeDto = changeDBOrder(orderVo);
        //修改es库存
        changeEsProd(changeDto.getEsChangeList());
        //生成订单信息
        createOrder(orderVo, wxUserId, orderNo);

        //超时订单处理 死信队列+延时队列
        overTimeOrder(orderNo, changeDto);
        //未支付消息通知
        messageOrder(orderNo, orderVo.getActualTotal());

        return orderNo;
    }

    /**
     * 订单消息为支付进行通知
     *
     * @param orderNo
     * @param actualTotal
     */
    private void messageOrder(String orderNo, BigDecimal actualTotal) {
        //从redis中获取到token
        String accessToken = stringRedisTemplate.opsForValue().get(OrderConstant.ACCESS_TOKEN_KEY).trim();
        String url = String.format(accessTokenUrl, accessToken);
        //通过消息队列一般发送消息
        HashMap<String, String> map = new HashMap<>();
        map.put("actualTotal", actualTotal.toString());
        map.put("templateId", templateId);
        map.put("accessTokenUrl", url);
        map.put("orderNo", orderNo);
        rabbitTemplate.convertAndSend(QueueConstant.WX_MSG_QUEUE, JSON.toJSONString(map));
    }

    private void overTimeOrder(String orderNo, ChangeDto changeDto) {
        //封装一个对象 保存orderNo changeDto  并将信息发送到延时队列
        OverTimerOrder overTimerOrder = new OverTimerOrder(orderNo, changeDto);
        rabbitTemplate.convertAndSend(QueueConstant.ORDER_MS_QUEUE, JSON.toJSONString(overTimerOrder));
    }

    /**
     * 生成主订单信息 和详细item信息
     *
     * @param orderVo
     * @param wxUserId
     * @param orderNo
     */
    private void createOrder(OrderVo orderVo, String wxUserId, String orderNo) {
        Order order = new Order();
        order.setUserId(wxUserId);
        order.setOrderNumber(orderNo);
        order.setTotal(orderVo.getTotalMoney());
        order.setActualTotal(orderVo.getActualTotal());
        order.setAddrOrderId(orderVo.getUserAddr().getAddrId());
        order.setProductNums(orderVo.getTotalCount());
        order.setCreateTime(new Date());
        order.setCancelTime(new Date());
        order.setFreightAmount(orderVo.getTransfee());
        order.setStatus(OrderStatusConstant.WAIT_PAY);
        order.setRemarks(orderVo.getRemarks());

        StringBuilder stringBuilder = new StringBuilder();

        ArrayList<OrderItem> orderItemList = new ArrayList<>();
        //设置orderItem
        orderVo.getShopCartOrders().stream().forEach(shopOrder -> {
            shopOrder.getShopCartItemDiscounts().stream().forEach(orderItem -> {
                //名字是使用逗号拼接的
                stringBuilder.append(orderItem.getProdName()).append(",");
                orderItem.setOrderNumber(orderNo);
                orderItem.setUserId(wxUserId);
                orderItem.setRecTime(new Date());
                BigDecimal totalAmount = orderItem.getPrice().multiply(BigDecimal.valueOf(orderItem.getProdCount()));
                orderItem.setProductTotalAmount(totalAmount);
                orderItemList.add(orderItem);
            });
        });
        //将最后个逗号去除
        String prodName = stringBuilder.replace(stringBuilder.length() - 1, stringBuilder.length(), "").toString();
        order.setProdName(prodName);
        orderMapper.insert(order);
        orderItemService.saveBatch(orderItemList);

    }

    /**
     * 更新es的库存
     *
     * @param esChangeList
     */
    private void changeEsProd(List<EsChange> esChangeList) {
        rabbitTemplate.convertAndSend(QueueConstant.ES_CHANGE_QUEUE, JSON.toJSONString(esChangeList));
    }

    //修改数据库的库存文件

    /**
     * 修改prod库存 和sku的库存
     * 需要判断数据库数据库存是否足够
     *
     * @param orderVo
     */
    private ChangeDto changeDBOrder(OrderVo orderVo) {

        HashMap<Long, Integer> map = new HashMap<>();
        List<SkuChang> skuChangList = new ArrayList<>();
        List<EsChange> esChangeList = new ArrayList<>();
        orderVo.getShopCartOrders().stream().forEach(shopOrder -> {
            shopOrder.getShopCartItemDiscounts().stream().forEach(orderItem -> {
                //获取到skuid 和数量
                Long skuId = orderItem.getSkuId();
                int number = orderItem.getProdCount() * -1;
                skuChangList.add(new SkuChang(skuId, number));

                Long prodId = orderItem.getProdId();
                //定义一个Map保存
                //此时用来判断订单是否回滚
                //此时的number需要带上符号
                if (map.containsKey(prodId)) {
                    map.put(prodId, map.get(prodId) + number);
                } else {  //没有回滚 直接
                    map.put(prodId, number);
                }
            });
        });
        map.forEach((prodId, number) -> {
            esChangeList.add(new EsChange(prodId, number));
        });
        ChangeDto changeDto = new ChangeDto(skuChangList, esChangeList);
        //封装一个对象 来保存prod和sku的商品id和购买数量
        //调用prod数据来改了
        orderProdFeign.changeDBOrder(changeDto);
        return changeDto;

    }

    //删除购物车
    private void removeBasket(OrderVo orderVo, String wxUserId) {
        ArrayList<Long> skuIds = new ArrayList<>();
        //根据用户id和skuid可以查出是否从购物车点击的
        orderVo.getShopCartOrders().stream().forEach(shopOrder -> {
            shopOrder.getShopCartItemDiscounts().stream().forEach(orderItem -> {
                Long skuId = orderItem.getSkuId();
                skuIds.add(skuId);
            });
        });
        //清除购车
        orderBasketFeign.removeCartById(skuIds, wxUserId);
    }

    //创建订单
    private String createOrderNo() {
        //机器标识 序列号
        return "xxx:" + new Snowflake(20L, 20L).nextIdStr();
    }
}




